using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace XDL.Framework.Collections
{
    public class UpdatableArray<T> where T : struct
    {
        public readonly T[] Items;
        int m_count = 0;

        public int Stride
        {
            get;
            private set;
        }

        /// <summary>
        /// Defines the update interface for this array
        /// </summary>
        public interface Client
        {
            /// <summary>
            /// Update function
            /// </summary>
            /// <param name="dt">Elapsed time</param>
            /// <param name="item">An instance of type T</param>
            /// <returns>true if the item is still active, false otherwise</returns>
            bool Update(GameTime gameTime, ref T item);

            /// <summary>
            /// Copy function
            /// </summary>
            /// <param name="item">Old item</param>
            /// <param name="targetItem">Relocated item</param>
            void Copy(ref T item, ref T targetItem);
        }

        Client m_client = null;

        public UpdatableArray(int size, Client client)
        {
            Stride = 1;
            Items = new T[size];
            m_client = client;
        }

        public UpdatableArray(int size, int stride, Client client)
        {
            Stride = stride;
            Items = new T[size];
            m_client = client;
        }


        /// <summary>
        /// "Clears" the array (e.g. set the number of active items to zero).
        /// </summary>
        public void Clear()
        {
            m_count = 0;
        }

        /// <summary>
        /// Returns the number of active items.
        /// </summary>
        public int ActiveItems
        {
            get
            {
                return m_count / Stride;
            }
        }

        /// <summary>
        /// Update the emitter state
        /// </summary>
        /// <param name="gameTime">Game time</param>
        public void Update(GameTime gameTime)
        {
            int n = m_count;
            int copyIndex = 0;
            for(int i = 0;i<n;i+=Stride)
            {
                if (m_client.Update(gameTime, ref Items[i]))
                {
                    // copy "duplicate" items from updated item
                    for (int s = 1; s < Stride; ++s)
                        m_client.Copy(ref Items[i], ref Items[i + s]);

                    // remove "blanks" in the array, in case previous entries have not been copied over
                    if (m_count != i)
                    {
                        for(int s = 0;s<Stride;++s)
                            Items[copyIndex + s] = Items[i + s];
                        
                    }
                    // particle is still active
                    copyIndex += Stride;
                }
            }
            System.Diagnostics.Debug.Assert(copyIndex <= m_count);
            System.Diagnostics.Debug.Assert(n == m_count,"Add(T item) called while updating the array.");
            m_count = copyIndex;
        }

        public bool Add(T item)
        {
            if (m_count < Items.Length)
            {
                Items[m_count] = item;
                m_count++;
                return true;
            }
            return false;
        }
    }
}
